package utils

import (
	"bytes"
	"compress/gzip"
	"crypto/md5"
	"encoding/binary"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"html/template"
	"io"
	"io/ioutil"
	"math/rand"
	"net"
	"net/http"
	"net/smtp"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"time"

	openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
	dysmsapi20170525 "github.com/alibabacloud-go/dysmsapi-20170525/v3/client"
	util "github.com/alibabacloud-go/tea-utils/v2/service"
	"github.com/alibabacloud-go/tea/tea"
	"github.com/forgoer/openssl"
	"github.com/gin-gonic/gin"
	uuid "github.com/satori/go.uuid"
	qrcode "github.com/skip2/go-qrcode"
	"github.com/spf13/viper"
	"golang.org/x/crypto/bcrypt"
)

/*
 +----------------------------------------------------------------------
 + Title        : 助手函数
 + Author       : huwl
 + Version      : 1.0.0
 + Initial-Time : 2021-09-22
 + Last-time    : 这个文件最后修改的时间 + 修改人的名称
 + Desc         : 助手函数
 +----------------------------------------------------------------------
*/

/**
 * 时间戳转换日期
 *
 * @param int timestamp -------------------------------------必传
 * @return string
 * @author huwl
 */
func UnixToDate(timestamp int) string {
	return time.Unix(int64(timestamp), 0).Format("2006-01-02 15:04:05")
}

/**
 * 日期转换时间戳
 *
 * @param string date ---------------------------------------必传
 * @return int64
 * @author huwl
 */
func DateToUnix(date string) int64 {
	t, err := time.ParseInLocation("2006-01-02 15:04:05", date, time.Local)
	if err != nil {
		return 0
	}

	return t.Unix()
}

/**
 * 获取时间戳
 *
 * @return int64
 * @author huwl
 */
func GetUnix() int64 {
	return time.Now().Unix()
}

/**
 * 获取纳秒数
 *
 * @return int64
 * @author huwl
 */
func GetUnixNano() int64 {
	return time.Now().UnixNano()
}

/**
 * 获取当前日期
 *
 * @return string
 * @author huwl
 */
func GetDate() string {
	return time.Now().Format("2006-01-02 15:04:05")
}

/**
 * 获取当前: 年-月-日
 *
 * @return string
 * @author huwl
 */
func GetDay() string {
	return time.Now().Format("2006-01-02")
}

/**
 * md5加密
 *
 * @param string value --------------------------------------必传
 * @return string
 * @author huwl
 */
func Md5Str(value string) string {
	h := md5.New()
	io.WriteString(h, value)
	return hex.EncodeToString(h.Sum(nil))
}

/**
 * bcrypt加密
 *
 * @param string value --------------------------------------必传
 * @return string, error
 * @author huwl
 */
func Bcrypt(value string) (string, error) {
	hash, err := bcrypt.GenerateFromPassword([]byte(value), bcrypt.DefaultCost)
	return string(hash), err
}

/**
 * bcrypt加密对比
 *
 * @param string hashValue ----------------------------------必传
 * @param string value --------------------------------------必传
 * @return bool
 * @author huwl
 */
func CompareBcrypt(hashValue, value string) bool {
	err := bcrypt.CompareHashAndPassword([]byte(hashValue), []byte(value))
	return err == nil
}

/**
 * 获取应用路径
 *
 * @return string
 * @author huwl
 */
func GetAppPath() string {
	file, _ := exec.LookPath(os.Args[0])
	path, _ := filepath.Abs(file)
	index := strings.LastIndex(path, string(os.PathSeparator))

	return path[:index]
}

/**
 * 获取配置数据
 *
 * @param string configName ---------------------------------必传
 * @param string configType ---------------------------------必传
 * @return *viper.Viper, error
 * @author huwl
 */
func GetConfig(configName, configType string) (*viper.Viper, error) {
	v := viper.New()
	// v.AddConfigPath(GetAppPath() + "/config/")
	v.AddConfigPath("./config/")
	v.SetConfigName(configName)
	v.SetConfigType(configType)
	err := v.ReadInConfig()
	return v, err
}

/**
 * 三目运算的函数
 *
 * @param bool a --------------------------------------------必传
 * @param interface{} b -------------------------------------必传
 * @param interface{} c -------------------------------------必传
 * @return interface{}
 * @author huwl
 */
func Ternary(a bool, b, c interface{}) interface{} {
	if a {
		return b
	}
	return c
}

/**
 * 分割时间2022-08-23T22:56:34+08:00 ----> 2022-08-23 22:56:34
 *
 * @param time.Time valueStr --------------------------------必传
 * @param int item ------------------------------------------必传
 * @return string
 * @author huwl
 */
func CutTime(valueStr time.Time) string {
	return valueStr.Format("2006-01-02 15:04:05")
}

/**
 * 是否包含字符串
 *
 * @param []string items ------------------------------------必传
 * @param int item ------------------------------------------必传
 * @return bool
 * @author huwl
 */
func IsContainStr(items []string, item string) bool {
	for _, eachItem := range items {
		if eachItem == item {
			return true
		}
	}
	return false
}

/**
 * 是否包含指定的数字
 *
 * @param []T items -------------------------必传
 * @param T item ---------------------------必传
 * @return bool
 * @author huwl
 */
func IsContainNumber[T uint8 | int8 | uint16 | int16 | uint32 | int32 | uint64 | int64 | uint | int](items []T, item T) bool {
	for _, eachItem := range items {
		if eachItem == item {
			return true
		}
	}
	return false
}

/**
 * 将 [](uint8|int8|uint16|int16|uint32|int32|uint64|int64) 转换为 []int
 *
 * @param []T src ---------------------------必传
 * @return []int
 * @author huwl
 */
func ConvertToInt[T uint8 | int8 | uint16 | int16 | uint32 | int32 | uint64 | int64](src []T) []int {
	dst := make([]int, len(src))
	for i, val := range src {
		dst[i] = int(val)
	}
	return dst
}

/**
 * 处理两位小数点
 *
 * @param interface{} value ---------------------------------必传
 * @return string
 * @author huwl
 */
func HandleTwoDecimalPoint(value interface{}) string {
	val, ok := value.(string)

	money := ""
	if ok {
		i, _ := strconv.Atoi(val)
		money = strconv.FormatFloat(float64(i)/100, 'f', 2, 64)
	}

	i, ok := value.(int)
	if ok {
		money = strconv.FormatFloat(float64(i)/100, 'f', 2, 64)
	}

	return money
}

/**
 * 运算
 *
 * @param interface{} n -------------------------------------必传
 * @param string operator -----------------------------------必传
 * @param interface{} m -------------------------------------必传
 * @return string
 * @author huwl
 */
func Operation(n interface{}, operator string, m interface{}) string {
	a, ok := n.(string)

	i := 0
	if ok {
		i, _ = strconv.Atoi(a)
	}

	b, ok := m.(string)
	z := 0
	if ok {
		z, _ = strconv.Atoi(b)
	}

	c, ok := n.(int)

	if ok {
		i = c
	}

	d, ok := m.(int)
	if ok {
		z = d
	}

	result := ""
	switch {
	case operator == "+":
		result = strconv.Itoa(i + z)
	case operator == "-":
		result = strconv.Itoa(i - z)
	case operator == "*":
		result = strconv.Itoa(i * z)
	case operator == "/":
		result = strconv.Itoa(i / z)
	}

	return result
}

/**
 * 拆分字符串
 *
 * @param string value --------------------------------------必传
 * @param string sep ----------------------------------------必传
 * @return []string
 * @author huwl
 */
func StringSplit(value, sep string) []string {
	return strings.Split(value, sep)
}

/**
 * string切片转换成int切片
 *
 * @param []string values -----------------------------------必传
 * @return []int
 * @author huwl
 */
func StringSliceToIntSlice(values []string) []int {
	data := []int{}
	for _, value := range values {
		i, _ := strconv.Atoi(value)
		data = append(data, i)
	}

	return data
}

/**
 * 字符串转换成html
 *
 * @param string value --------------------------------------必传
 * @return template.HTML
 * @author huwl
 */
func StrToHtml(value string) template.HTML {
	return template.HTML(value)
}

/**
 * 验证手机号
 *
 * @param string mobile -------------------------------------必传
 * @return bool
 * @author huwl
 */
func CheckMobile(mobile string) bool {
	if mobile == "" {
		return false
	}
	pattern := `^1[3|5|7|8|9][\d]{9}$`
	reg := regexp.MustCompile(pattern)
	return reg.MatchString(mobile)
}

/**
 * 删除Proto json omitempty
 *
 * @param string filename -----------------------------------必传
 * @author huwl
 */
func RemoveProtoJsonOmitempty(filename string) {
	contents, err := ioutil.ReadFile(filename)
	if err != nil {
		fmt.Println("ioutil ReadFile error：", err.Error())
		return
	}

	data := strings.ReplaceAll(string(contents), ",omitempty", "")
	err = ioutil.WriteFile(filename, []byte(data), 0755)
	if err != nil {
		fmt.Println("ioutil WriteFile error：", err.Error())
		return
	}

	fmt.Println("remove proto json omitempty successfully!!!")
}

/**
 * 分割数字
 *
 * @param string str ----------------------------------------必传
 * @param strin delimiter -----------------------------------必传
 * @return []int
 * @author huwl
 */
func IntSplit(str string, delimiter string) []int {
	contents := strings.Split(str, delimiter)
	numbers := []int{}
	for _, content := range contents {
		i, _ := strconv.Atoi(content)
		numbers = append(numbers, i)
	}

	return numbers
}

/**
 * 生成二维码
 *
 * @param *gin.Context ctx ----------------------------------必传
 * @param string contents -----------------------------------必传
 * @param string mode ---------------------------------------必传
 * @return string, error
 * @author huwl
 */
func GenerateQrcode(ctx *gin.Context, contents, mode string) (string, error) {
	if mode == "image" {
		cfg, err := GetConfig("app", "yml")
		if err != nil {
			return "", errors.New("读取APP配置文件")
		}

		savePath := cfg.GetString("dev.qrcode_save_path")
		err = qrcode.WriteFile(contents, qrcode.Medium, 256, savePath)
		if err != nil {
			return "", err
		}

		b, err := ioutil.ReadFile(savePath)
		if err != nil {
			return "", err
		}

		return string(b), nil
	}

	var png []byte
	png, err := qrcode.Encode(contents, qrcode.Medium, 256)
	if err != nil {
		return "", err
	}

	return string(png), nil
}

/**
 * 验证邮箱
 *
 * @param string email --------------------------------------必传,邮箱
 * @return bool
 * @author huwl
 */
func CheckEmail(email string) bool {
	if email == "" {
		return false
	}
	pattern := `^[0-9a-z][_.0-9a-z-]{0,31}@([0-9a-z][0-9a-z-]{0,30}[0-9a-z]\.){1,4}[a-z]{2,4}$`
	reg := regexp.MustCompile(pattern)
	return reg.MatchString(email)
}

/**
 * HttpPostForm
 *
 * @param string requestUrl ---------------------------------必传,请求地址
 * @param map[string]string params --------------------------必传,请求参数
 * @return map[string]interface error
 * @author huwl
 */
func HttpPostForm(requestUrl string, params map[string]string) (map[string]interface{}, error) {
	requestParams := url.Values{}

	// 组装请求参数
	for k, v := range params {
		requestParams.Add(k, v)
	}

	// 发起post请求
	resp, err := http.PostForm(requestUrl, requestParams)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()
	// 读取数据
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	result := map[string]interface{}{}
	err = json.Unmarshal(body, &result)
	if err != nil {
		return nil, err
	}
	return result, nil
}

/**
 * HttpGet
 *
 * @param string requestUrl ---------------------------------必传,请求地址
 * @param map[string]string params --------------------------必传,请求参数
 * @return map[string]interface error
 * @author huwl
 */
func HttpGet(requestUrl string, params map[string]string) (map[string]interface{}, error) {
	uri := ""

	// 组装请求参数
	for k, v := range params {
		uri += k + "=" + v + "&"
	}

	// 发起post请求
	resp, err := http.Get(requestUrl + uri)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()
	// 读取数据
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	result := map[string]interface{}{}
	err = json.Unmarshal(body, &result)
	if err != nil {
		return nil, err
	}
	return result, nil
}

/**
 * 生成随机字符串
 *
 * @param int length ----------------------------------------必传,长度
 * @return string
 * @author huwl
 */
func GenerateRandomStr(length int) string {
	// 长度为62
	var bytes []byte = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890")
	result := make([]byte, length)
	rand.Seed(time.Now().UnixNano())
	for i := 0; i < length; i++ {
		result[i] = bytes[rand.Int31()%62]
	}
	return "tm_" + string(result)
}

/**
 * 生成验证码
 *
 * @param int length ----------------------------------------必传,验证码长度
 * @param int t ---------------------------------------------必传,验证码类型
 * @return string
 * @author huwl
 */
func GenerateCode(length, t int) string {
	divisor := ""
	idxBits := 0
	switch t {
	case 0:
		fallthrough
	case 1:
		divisor = "0123456789"
		idxBits = 4
	case 2:
		divisor = "abcdefghijklmnopqrstuvwxyz"
		idxBits = 5
	case 3:
		divisor = "0123456789abcdefghijklmnopqrstuvwxyz"
		idxBits = 6
	}

	code := make([]byte, length)

	// 利用当前时间的UNIX时间戳初始化rand包
	rand.Seed(time.Now().UnixNano())

	idxMask := 1<<idxBits - 1
	idxMax := 63 / idxBits
	for i, cache, remain := 0, rand.Int63(), idxMax; i < length; {
		if remain == 0 {
			cache, remain = rand.Int63(), idxMax
		}

		randIndex := int(cache & int64(idxMask))
		if randIndex < len(divisor) {
			code[i] = divisor[randIndex]
			i++
		}

		// 使用下一组
		cache >>= int64(idxBits)
		remain--
	}

	return string(code)
}

/**
 * 发邮件
 *
 * @param string toEmail ------------------------------------必传,收件人邮箱
 * @param string subject ------------------------------------必传,主题
 * @param string body ---------------------------------------必传,内容
 * @param string mailType -----------------------------------必传,邮件类型
 * @return error
 * @author huwl
 */
func SendEmail(toEmail, subject, body, mailType string) error {
	//读取 email 配置
	cfg, err := GetConfig("email", "yml")
	if err != nil {
		return err
	}

	username := cfg.GetString(UserSrvEnv + ".username")
	password := cfg.GetString(UserSrvEnv + ".password")
	host := cfg.GetString(UserSrvEnv + ".host")

	// 拼接消息体
	var contentType string
	if mailType == "html" {
		contentType = "Content-Type: text/" + mailType + "; charset=UTF-8"
	} else {
		contentType = "Content-Type: text/plain" + "; charset=UTF-8"
	}

	auth := smtp.PlainAuth("", username, password, host)
	msg := []byte("To: " + toEmail + "\r\nFrom: " + username + "\r\nSubject: " + subject + "\r\n" + contentType + "\r\n\r\n" + body)
	return smtp.SendMail(host+":25", auth, username, []string{toEmail}, msg)
}

/**
 * 发短信
 *
 * @param string mobile -------------------------------------必传,手机号
 * @param string template -----------------------------------必传,模板
 * @param map[string]interface{} params ---------------------必传,参数
 *  {"name":"张三","number":"1390000****"}、{"code":"1111"}
 * @return error
 * @author huwl
 */
func SendSms(mobile, template string, params map[string]interface{}) error {
	//读取 sms 配置
	cfg, err := GetConfig("sms", "yml")
	if err != nil {
		return err
	}

	accessKeyId := cfg.GetString(UserSrvEnv + ".accessKeyId")
	accessKeySecret := cfg.GetString(UserSrvEnv + ".accessKeySecret")
	endpoint := cfg.GetString(UserSrvEnv + ".endpoint")
	signName := cfg.GetString(UserSrvEnv + ".signName")
	templateCode := cfg.GetString(template)
	templateParam, _ := json.Marshal(params)

	config := &openapi.Config{
		// 必填，您的 AccessKey ID
		AccessKeyId: tea.String(accessKeyId),
		// 必填，您的 AccessKey Secret
		AccessKeySecret: tea.String(accessKeySecret),
	}
	// Endpoint 请参考 https://api.aliyun.com/product/Dysmsapi
	config.Endpoint = tea.String(endpoint)
	client, err := dysmsapi20170525.NewClient(config)
	if err != nil {
		return err
	}

	sendSmsRequest := &dysmsapi20170525.SendSmsRequest{
		PhoneNumbers:  tea.String(mobile),
		SignName:      tea.String(signName),
		TemplateCode:  tea.String(templateCode),
		TemplateParam: tea.String(string(templateParam)),
	}

	result, err := client.SendSmsWithOptions(sendSmsRequest, &util.RuntimeOptions{})
	if err != nil {
		return err
	}
	body := *result.Body
	if *body.Code != "OK" {
		return errors.New(*result.Body.Message)
	}

	return nil
}

/**
 * zip压缩
 *
 * @param []byte data ---------------------------------------必传
 * @return []byte, error
 * @author huwl
 */
func Zip(data []byte) ([]byte, error) {
	var b bytes.Buffer
	gz, _ := gzip.NewWriterLevel(&b, 9)
	_, err := gz.Write([]byte(data))
	if err != nil {
		return nil, err
	}
	err = gz.Flush()
	if err != nil {
		return nil, err
	}
	err = gz.Close()
	if err != nil {
		return nil, err
	}

	return b.Bytes(), nil
}

/**
 * zip解压
 *
 * @param []byte data ---------------------------------------必传
 * @return []byte, error
 * @author huwl
 */
func UnZip(data []byte) ([]byte, error) {
	b := new(bytes.Buffer)
	err := binary.Write(b, binary.LittleEndian, data)
	if err != nil {
		return nil, err
	}

	r, err := gzip.NewReader(b)
	if err != nil {
		return nil, err
	}

	defer r.Close()

	unzipData, err := ioutil.ReadAll(r)
	if err != nil {
		return nil, err
	}

	return unzipData, nil
}

/**
 * AesCBC 加密
 *
 * @param []byte src ----------------------------------------必传
 * @param []byte key ----------------------------------------必传
 * @param []byte iv -----------------------------------------必传
 * @param string padding ------------------------------------必传
 * @return []byte, error
 * @author huwl
 */
func AesCBCEncrypt(src, key, iv []byte, padding string) ([]byte, error) {
	data, err := openssl.AesCBCEncrypt(src, key, iv, padding)
	if err != nil {
		return nil, err
	}

	return []byte(hex.EncodeToString(data)), nil
}

/**
 * AesCBC 解密
 *
 * @param []byte src ----------------------------------------必传
 * @param []byte key ----------------------------------------必传
 * @param []byte iv -----------------------------------------必传
 * @param string padding ------------------------------------必传
 * @return []byte, error
 * @author huwl
 */
func AesCBCDecrypt(src, key, iv []byte, padding string) ([]byte, error) {
	data, err := hex.DecodeString(string(src))
	if err != nil {
		return nil, err
	}

	return openssl.AesCBCDecrypt(data, key, iv, padding)
}

/**
 * 创建uuid
 *
 * https://github.com/satori/go.uuid
 * @return string
 * @author huwl
 */
func CreateUuid() string {
	return uuid.NewV4().String()
}

/**
 * 检查目录是否存在，如果不存在则自动创建。
 *
 * @param string logPath ------------------------------------------------必传
 * @return error
 * @author huwl
 */
func EnsureDir(logPath string) error {
	dir := filepath.Dir(logPath)
	if _, err := os.Stat(dir); os.IsNotExist(err) {
		return os.MkdirAll(dir, 0755)
	}

	return nil
}

/**
 * 获取所有IPv4地址（排除回环地址）
 *
 * @return []string
 * @author huwl
 */
func GetIPAddresses() []string {
	var ips []string

	interfaces, err := net.Interfaces()
	if err != nil {
		return ips
	}

	for _, iface := range interfaces {
		// 跳过非运行状态和回环接口
		if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagLoopback != 0 {
			continue
		}

		addrs, err := iface.Addrs()
		if err != nil {
			continue
		}

		for _, addr := range addrs {
			var ip net.IP
			switch v := addr.(type) {
			case *net.IPNet:
				ip = v.IP
			case *net.IPAddr:
				ip = v.IP
			}

			// 只获取IPv4地址
			if ip == nil || ip.IsLoopback() {
				continue
			}
			ip = ip.To4()
			if ip == nil {
				continue
			}

			ips = append(ips, fmt.Sprintf("%s (%s)", ip.String(), iface.Name))
		}
	}

	return ips
}

/**
 * 获取服务IP
 *
 * @return string, error
 * @author huwl
 */
func GetServiceIP() (string, error) {
	if IsRunningInContainer() {
		// 容器环境下的IP获取策略
		return GetContainerIP()
	} else {
		// 非容器环境下的IP获取策略
		return ServiceHost, nil
		// return GetLocalIP()
	}
}

/**
 * 获取本地IP（通用方法）
 *
 * @return string, error
 * @author huwl
 */
func GetLocalIP() (string, error) {
	addrs, err := net.InterfaceAddrs()
	if err != nil {
		return "", err
	}

	for _, addr := range addrs {
		if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
			if ipnet.IP.To4() != nil {
				return ipnet.IP.String(), nil
			}
		}
	}

	return "", errors.New("no non-loopback IPv4 address found")
}

/**
 * 获取Docker容器IP
 *
 * @return string, error
 * @author huwl
 */
func GetContainerIP() (string, error) {
	// 1. 首先尝试环境变量
	if ip := os.Getenv("SERVICE_IP"); ip != "" {
		return ip, nil
	}

	// 2. 尝试hostname命令
	if ip, err := exec.Command("hostname", "-i").Output(); err == nil {
		return strings.TrimSpace(string(ip)), nil
	}

	// 3. 尝试网络接口检测
	if ip, err := GetDockerIP(); err == nil {
		return ip, nil
	}

	return "", fmt.Errorf("无法获取容器IP")
}

/**
 * 获取DockerIP
 *
 * @return string, error
 * @author huwl
 */
func GetDockerIP() (string, error) {
	addrs, err := net.InterfaceAddrs()
	if err != nil {
		return "", err
	}

	for _, addr := range addrs {
		if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
			if ipnet.IP.To4() != nil && !IsDefaultDockerNetwork(ipnet.IP) {
				return ipnet.IP.String(), nil
			}
		}
	}

	return "", errors.New("no suitable IP found")
}

/**
 * 是否默认docker网络
 *
 * @param net.IP ip -----------------------------------------------------必传
 * @return bool
 * @author huwl
 */
func IsDefaultDockerNetwork(ip net.IP) bool {
	// 排除Docker默认网段
	dockerNetworks := []string{"172.17.", "172.18.", "172.19.", "192.168."}
	ipStr := ip.String()

	for _, network := range dockerNetworks {
		if strings.HasPrefix(ipStr, network) {
			return true
		}
	}
	return false
}

/**
 * 是否运行于docker容器
 *
 * @return bool
 * @author huwl
 */
func IsRunningInContainer() bool {
	// 方法1: 检查 /.dockerenv 文件
	if _, err := os.Stat("/.dockerenv"); err == nil {
		return true
	}

	// 方法2: 检查 /proc/1/cgroup
	if cgroupData, err := os.ReadFile("/proc/1/cgroup"); err == nil {
		content := string(cgroupData)
		if strings.Contains(content, "docker") ||
			strings.Contains(content, "kubepods") ||
			strings.Contains(content, "containerd") {
			return true
		}
	}

	// 方法3: 检查容器特定的环境变量
	containerEnvVars := []string{
		"KUBERNETES_SERVICE_HOST", // Kubernetes
		"CONTAINER_ID",            // 一些容器平台
		"DOCKER_CONTAINER",        // Docker
	}

	for _, envVar := range containerEnvVars {
		if os.Getenv(envVar) != "" {
			return true
		}
	}

	return false
}
